package com.wu.client.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.wu.client.constant.RelationShipTypeEnum;
import com.wu.client.constant.ResTypeEnum;
import com.wu.client.dao.redis.Likes;
import com.wu.client.service.CommunityService;
import com.wu.client.service.RelationshipService;
import com.wu.client.service.UserLikesService;
import com.wu.client.utils.MinIoUtil;
import com.wu.client.utils.PageRespUtil;
import com.wu.client.vo.req.post.PostAddReq;
import com.wu.client.vo.res.PageResp;
import com.wu.client.vo.res.community.PostResp;
import com.wu.common.convertion.exception.ClientException;
import com.wu.constant.Enum.MinIoNameEnum;
import com.wu.mongo.MainComment;
import com.wu.mongo.Post;
import com.wu.mysql.entiy.*;
import com.wu.mysql.mapper.*;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.bson.types.ObjectId;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.wu.client.utils.FileUtil.decodeBase64;

/**
 * @author Cousin
 * @date 2025/4/7
 * @Description
 */
@Service
@RequiredArgsConstructor
public class CommunityServiceImpl implements CommunityService {
    private final MinIoUtil minIoUtil;
    private final MongoTemplate mongoTemplate;
    private final MvMapper mvMapper;
    private final SongMapper songMapper;
    private final AlbumMapper albumMapper;
    private final RelationshipService relationshipService;
    private final UserMapper userMapper;
    private final UserLikesService userLikesService;
    private final PlaylistMapper playlistMapper;

    @SneakyThrows
    @Override
    public void addPost(PostAddReq post) {
        Post postMongo = new Post();
        Date date = new Date();
        postMongo.setCreatedTime(date);
        postMongo.setContent(post.getContent());
        postMongo.setUserId(post.getUserId());
        postMongo.setMediaType(post.getMediaType());
        postMongo.setLocation(post.getLocation());
        postMongo.setVisibility(post.getVisibility());

        ResTypeEnum resTypeEnum = ResTypeEnum.getByCode(post.getMediaType());
        Boolean isExternal = post.getMediaData().getIsExternal();

        switch (resTypeEnum) {
            case IMAGE:
                handleImagePost(post, postMongo);
                break;
            case SONG:
                handleSongPost(post, postMongo, isExternal);
                break;
            case MV:
                handleMvPost(post, postMongo, isExternal);
                break;
            case PLAYLIST,ALBUM:
                postMongo.setResId(post.getMediaData().getId());
                break;
            default:
                throw new ClientException("类型错误");
        }
        mongoTemplate.insert(postMongo);
    }

    // 处理图片
    private void handleImagePost(PostAddReq post, Post postMongo) throws Exception {
        List<PostAddReq.MediaData> images = post.getMediaData().getImages();
        if (CollectionUtil.isNotEmpty(images)) {
            StringBuilder fileName = new StringBuilder();
            for (PostAddReq.MediaData image : images) {
                String imgFileName = processMediaFile(image.getBase64(), image.getSuffix(), MinIoNameEnum.COMMUNITY_IMAGE);
                fileName.append(imgFileName).append("/");
            }
            postMongo.setFileName(!fileName.isEmpty() ? fileName.substring(0, fileName.length() - 1) : "");
        }
    }

    // 处理歌曲
    private void handleSongPost(PostAddReq post, Post postMongo, Boolean isExternal) throws Exception {
        if (isExternal) {
            String songFileName = processMediaFile(post.getMediaData().getBase64(), post.getMediaData().getSuffix(), MinIoNameEnum.SONG);
            postMongo.setFileName(songFileName);
        } else {
            Long resId = post.getMediaData().getId();
            postMongo.setResId(resId);
        }
    }

    // 处理MV
    private void handleMvPost(PostAddReq post, Post postMongo, Boolean isExternal) throws Exception {
        if (isExternal) {
            String mvFileName = processMediaFile(post.getMediaData().getBase64(), post.getMediaData().getSuffix(), MinIoNameEnum.MV);
            postMongo.setFileName(mvFileName);
        } else {
            Long resId = post.getMediaData().getId();
            postMongo.setResId(resId);
        }
    }

    // 处理媒体文件的上传
    private String processMediaFile(String base64Data, String suffix, MinIoNameEnum bucketName) throws Exception {
        byte[] audioBytes = decodeBase64(base64Data);
        InputStream fileStream = new ByteArrayInputStream(audioBytes);
        String fileName = DigestUtil.md5Hex(audioBytes) + suffix;
        boolean fileExist = minIoUtil.doesFileExist(bucketName.getBucketName(), fileName);
        if (fileExist) {
            return fileName;  // 如果文件已存在，则返回 null，避免重复上传
        }
        minIoUtil.uploadFile(bucketName.getBucketName(), fileName, fileStream, audioBytes.length);
        return fileName;
    }


    @Override
    public PageResp<PostResp> getPosts(Integer page, Integer limit, Long userId, String type) {
        PageRequest pageRequest = PageRequest.of(page - 1, limit);
        Query query = new Query().with(pageRequest);
        return getPostRespPageResp(page, limit, userId, query);
    }

    @SneakyThrows
    @Override
    public void deletePost(String postId) {
        Query query = new Query(Criteria.where("id").is(new ObjectId(postId)));
        Post one = mongoTemplate.findOne(query, Post.class);
        if (ObjectUtil.isNotNull(one)){
            if (ObjectUtil.isNotNull(one.getResId())){
                Integer mediaType = one.getMediaType();
                ResTypeEnum resTypeEnum = ResTypeEnum.getByCode(mediaType);
                switch (resTypeEnum){
                    case SONG:
                        if (ObjectUtil.isNull(one.getResId())){
                            minIoUtil.deleteFile(MinIoNameEnum.SONG.getBucketName(), one.getFileName());
                        }
                        break;
                    case MV:
                        if (ObjectUtil.isNull(one.getResId())){
                            minIoUtil.deleteFile(MinIoNameEnum.MV.getBucketName(), one.getFileName());
                        }
                        break;
                    case IMAGE:
                        for (String s : one.getFileName().split("/")) {
                            minIoUtil.deleteFile(MinIoNameEnum.COMMUNITY_IMAGE.getBucketName(), s);
                        }
                        break;
                }
            }
            mongoTemplate.remove(query, Post.class);
        }


    }

    @Override
    public PageResp<PostResp> getUserPosts(Integer page, Integer limit, Long currentUserId, Long userId) {
        PageRequest pageRequest = PageRequest.of(page - 1, limit);
        Query query = new Query()
                .addCriteria(Criteria.where("userId").is(userId)).with(pageRequest);
        return getPostRespPageResp(page, limit, currentUserId, query);
    }

    @NotNull
    private PageResp<PostResp> getPostRespPageResp(Integer page, Integer limit, Long currentUserId, Query query) {
        long count = mongoTemplate.count(query, Post.class);
        if (count <= 0) {
            return new PageResp<>();
        }

        List<Post> posts = mongoTemplate.find(query, Post.class);
        List<String> postIds = new ArrayList<>();

        if (CollectionUtil.isNotEmpty(posts)) {
            List<PostResp> postRespList = posts.stream()
                    .filter(post -> StringUtil.isNotEmpty(post.getFileName()))
                    .map(post -> processPost(post, postIds))
                    .collect(Collectors.toList());

            Map<String, Likes> likesMap = userLikesService.checkUserIfLike(ResTypeEnum.COMMUNITY, currentUserId, postIds);

            postRespList.forEach(postResp -> {
                Likes likes = likesMap.get(postResp.getId());
                if (likes != null) {
                    postResp.setIsLiked(likes.getIsLiked());
                    postResp.setLikesCount(likes.getCount());
                }
            });

            return PageRespUtil.buildPageResp(page, limit, count, postRespList);
        }

        return new PageResp<>();
    }

    private PostResp processPost(Post post, List<String> postIds) {
        PostResp postResp = BeanUtil.toBean(post, PostResp.class);
        postIds.add(postResp.getId());



        PostResp.MediaInfo mediaInfo = createMediaInfo(post);

        postResp.setMediaInfo(mediaInfo);
        UserDO userDO = userMapper.selectById(post.getUserId());
        postResp.setUserId(post.getUserId());
        postResp.setUsername(userDO.getUsername());
        postResp.setAvatar(userDO.getAvatar());
        Integer resType = ResTypeEnum.COMMUNITY.getCode();
        Query commentQuery = new Query(Criteria.where("contentType").is(resType).and("contentId").is(postResp.getId()));
        postResp.setCommentsCount(mongoTemplate.count(commentQuery, MainComment.class));
        return postResp;
    }

    private PostResp.MediaInfo createMediaInfo(Post post) {
        PostResp.MediaInfo mediaInfo = new PostResp.MediaInfo();
        String url = "";
        ResTypeEnum resTypeEnum = ResTypeEnum.getByCode(post.getMediaType());
        switch (resTypeEnum) {
            case IMAGE:
                url = processImage(post, mediaInfo);
                break;
            case SONG:
                url = processSong(post, mediaInfo);
                break;
            case MV:
                url = processMV(post, mediaInfo);
                break;
            case ALBUM:
                processAlbum(post, mediaInfo);
                break;
            case PLAYLIST:
                processPlaylist(post, mediaInfo);
                break;
            default:
                break;
        }

        mediaInfo.setUrl(url);
        mediaInfo.setCover("/img/cover/1.jpg");  // Default cover image
        return mediaInfo;
    }

    private String processImage(Post post, PostResp.MediaInfo mediaInfo) {
        String[] split = post.getFileName().split("/");
        List<PostResp.Image> images = new ArrayList<>();
        for (String s : split) {
            String url = minIoUtil.getFileUrlDefault(MinIoNameEnum.COMMUNITY_IMAGE.getBucketName(), s);
            images.add(new PostResp.Image(url));
        }
        mediaInfo.setImages(images);
        return images.isEmpty() ? "" : images.get(0).getUrl();
    }

    private String processSong(Post post, PostResp.MediaInfo mediaInfo) {
        String fileName;
        Long resId = post.getResId();
        if (ObjectUtil.isNotNull(resId)) {
            SongDO song = songMapper.selectById(resId);
            fileName = song.getFileName();
            mediaInfo.setTitle(song.getTitle());
            String artisName = relationshipService.getArtisName(resId, RelationShipTypeEnum.ARTIST_SONG);
            mediaInfo.setArtists(artisName);
        } else {
            fileName = post.getFileName();
        }
        return minIoUtil.getFileUrlDefault(MinIoNameEnum.SONG.getBucketName(), fileName);
    }



    private String processMV(Post post, PostResp.MediaInfo mediaInfo) {
        Long resId = post.getResId();
        String fileName;
        if (ObjectUtil.isNotNull(resId)) {
            MvDO mvDO = mvMapper.selectById(resId);
            fileName = mvDO.getFileName();
            mediaInfo.setTitle(mvDO.getTitle());
            String artisName = relationshipService.getArtisName(resId, RelationShipTypeEnum.ARTIST_MV);
            mediaInfo.setArtists(artisName);
        } else {
            fileName = post.getFileName();
        }
        return minIoUtil.getFileUrlDefault(MinIoNameEnum.MV.getBucketName(), fileName);
    }



    private void processAlbum(Post post, PostResp.MediaInfo mediaInfo) {
        Long resId = post.getResId();
        if (ObjectUtil.isNotNull(resId)) {
            AlbumDO albumDO = albumMapper.selectById(resId);
            mediaInfo.setTitle(albumDO.getTitle());
            String artisName = relationshipService.getArtisName(resId, RelationShipTypeEnum.ARTIST_ALBUM);
            mediaInfo.setArtists(artisName);
        }
    }

    private void processPlaylist(Post post, PostResp.MediaInfo mediaInfo) {
        Long resId = post.getResId();
        if (ObjectUtil.isNotNull(resId)) {
            PlaylistDO playlistDO = playlistMapper.selectById(resId);
            mediaInfo.setTitle(playlistDO.getTitle());
            mediaInfo.setArtists(playlistDO.getCreator());
            mediaInfo.setId(resId);
            mediaInfo.setArtists(playlistDO.getCreator());
        }
    }
    }

